// Enter your shader in this window
#define nThread 128

Buffer<uint> g_V2FCountBuffer;
Buffer<uint> g_V2FCountSumBuffer;
Buffer<uint> g_V2FIndexBuffer;

RWBuffer<uint> g_V2FCountBuffer_RW;

RWBuffer<uint> g_V2FIndexBuffer_RW;
RWBuffer<uint> g_tmpCount_RW;
Buffer<uint> g_indexBuffer;


uint nGroupX = 1;
uint nGroupY = 1;
uint nGroupZ = 1;
//remember to call ClearUnorderedAccessView first to clear the g_V2FCountBuffer
[numthreads( nThread,  1,  1)] //ThreadGroupCount = ceil(numFace / nThread);
void cs_FillV2FCount(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint faceIdx = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	
	uint numFace = 0;
	g_indexBuffer.GetDimensions(numFace);

    numFace /= 3;
	if (faceIdx >= numFace)
		return;
	uint vtxIdxs[3] = {g_indexBuffer[faceIdx*3], g_indexBuffer[faceIdx*3+1], g_indexBuffer[faceIdx*3+2]};

	for (int i =0; i < 3; i++)
	{
		uint orgValue = 0;
		InterlockedAdd(g_V2FCountBuffer_RW[vtxIdxs[i]], 1, orgValue);
    }
}

//remember to call ClearUnorderedAccessView first to clear the g_V2FCountBuffer
[numthreads( nThread,  1,  1)] //ThreadGroupCount = ceil((numFace / nThread);
void cs_FillV2FIndex(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint faceIdx = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	
	uint numFace = 0;
	g_indexBuffer.GetDimensions(numFace);
    numFace /= 3;
	if (faceIdx >= numFace)
		return;
	uint vtxIdxs[3] = {g_indexBuffer[faceIdx*3], g_indexBuffer[faceIdx*3+1], g_indexBuffer[faceIdx*3+2]};
	for (int i =0; i < 3; i++)
	{
		uint c = 0;
		InterlockedAdd(g_tmpCount_RW[vtxIdxs[i]], 1, c);
		uint startIdx = g_V2FCountSumBuffer[vtxIdxs[i]] - g_V2FCountBuffer[vtxIdxs[i]];
		g_V2FIndexBuffer_RW[startIdx + c] = faceIdx;
		
    }
}


struct IndexedEdge
{
	uint orgIdx;
	uint iStart;
	uint iEnd;
};
StructuredBuffer<IndexedEdge> g_PreEdgeIndex;
RWStructuredBuffer<IndexedEdge> g_PreEdgeIndex_RW;


[numthreads( nThread,  1,  1)] 
void cs_FillPreEdgeIndexBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint faceIdx = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint numFace = 0;
	g_indexBuffer.GetDimensions(numFace);
    numFace /= 3;
	
	if (faceIdx >= numFace)
		return ;
		
	uint vIdx[3];
	for (int j =0; j < 3; j++)
	{
		vIdx[j] = g_indexBuffer[faceIdx*3+j];
	}
	g_PreEdgeIndex_RW[faceIdx*3+0].orgIdx = faceIdx*3+0;
	g_PreEdgeIndex_RW[faceIdx*3+0].iStart = min(vIdx[0], vIdx[1]);
	g_PreEdgeIndex_RW[faceIdx*3+0].iEnd = max(vIdx[0], vIdx[1]);
	
	g_PreEdgeIndex_RW[faceIdx*3+1].orgIdx = faceIdx*3+1;
	g_PreEdgeIndex_RW[faceIdx*3+1].iStart = min(vIdx[0], vIdx[2]);
	g_PreEdgeIndex_RW[faceIdx*3+1].iEnd = max(vIdx[0], vIdx[2]);

	g_PreEdgeIndex_RW[faceIdx*3+2].orgIdx = faceIdx*3+2;
	g_PreEdgeIndex_RW[faceIdx*3+2].iStart = min(vIdx[1], vIdx[2]);
	g_PreEdgeIndex_RW[faceIdx*3+2].iEnd = max(vIdx[1], vIdx[2]);
	
}
Buffer<uint> g_preEdgeIndexDuplicateBuf;
Buffer<uint> g_preEdgeIndexDuplicateSumBuf;
Buffer<uint> g_edgeIndexBuf;
RWBuffer<uint> g_preEdgeIndexDuplicateBuf_RW;
RWBuffer<uint> g_edgeIndexBuf_RW;

[numthreads( nThread,  1,  1)]  // Thread number = nface*3
void cs_FillPreEdgeIndexDuplicateCountBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint maxThread = 0;
	uint szStride = 0;
	g_preEdgeIndexDuplicateBuf_RW.GetDimensions(maxThread);
	if (nTh >= maxThread)
		return ;
	g_preEdgeIndexDuplicateBuf_RW[nTh] = 0;
	if (nTh != 0)
	{
		IndexedEdge curEdge = g_PreEdgeIndex[nTh];
		IndexedEdge lastEdge = g_PreEdgeIndex[nTh-1];
		if (curEdge.iStart == lastEdge.iStart && curEdge.iEnd == lastEdge.iEnd)
		{
			g_preEdgeIndexDuplicateBuf_RW[nTh] = 1;
		}		
	}

	return;
}

[numthreads( nThread,  1,  1)] 
void cs_FillEdgeIndexBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint maxThread = 0;
	uint szStride = 0;
	g_preEdgeIndexDuplicateBuf.GetDimensions(maxThread);
	if (nTh >= maxThread)
		return ;
	if (g_preEdgeIndexDuplicateBuf[nTh] == 0)
	{
		uint fillIdx = nTh - g_preEdgeIndexDuplicateSumBuf[nTh];
		g_edgeIndexBuf_RW[fillIdx*2] = g_PreEdgeIndex[nTh].iStart;
		g_edgeIndexBuf_RW[fillIdx*2+1] = g_PreEdgeIndex[nTh].iEnd;
	}
	return ;
}
Buffer<uint> g_V2ECountBuf;
Buffer<uint> g_V2ECountSumBuf;
RWBuffer<uint> g_V2ECountBuf_RW;

Buffer<uint> g_Vertex2EdgeBuf;
RWBuffer<uint> g_Vertex2EdgeBuf_RW;

[numthreads( nThread,  1,  1)] 
void cs_FillVertex2EdgeCountBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nEdge = 0;
	g_edgeIndexBuf.GetDimensions(nEdge);
	nEdge /= 2;
	
	if (nTh >= nEdge)
		return ;
	for (uint j =0; j<2; j++)
	{
		uint idx = nTh*2 + j;
		uint c = 0;
		InterlockedAdd(g_V2ECountBuf_RW[g_edgeIndexBuf[idx]], 1, c);
	}
}

[numthreads( nThread,  1,  1)] 
void cs_FillVertex2EdgeBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nEdge = 0;
	g_edgeIndexBuf.GetDimensions(nEdge);
	nEdge /= 2;
	if (nTh >= nEdge)
		return ;
		
	for (int j =0; j<2; j++)
	{
		uint idx = nTh*2 + j;
		uint vidx = g_edgeIndexBuf[idx];
		uint c = 0;
		InterlockedAdd(g_tmpCount_RW[vidx], 1, c);
		uint fillIdx = g_V2ECountSumBuf[vidx] - g_V2ECountBuf[vidx] + c ;
		g_Vertex2EdgeBuf_RW[fillIdx] = nTh;
	}
}

Buffer<uint> g_face2EdgeBuf;
RWBuffer<uint> g_face2EdgeBuf_RW;

[numthreads( nThread,  1,  1)]  // nFace*3
void cs_FillFace2EdgeBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nEntry = 0;
	uint szStride = 0;
	g_PreEdgeIndex.GetDimensions(nEntry, szStride);
	if (nTh >= nEntry)
		return ;
	uint orgIdx = g_PreEdgeIndex[nTh].orgIdx;
	uint newIdx = nTh - g_preEdgeIndexDuplicateSumBuf[nTh];
	g_face2EdgeBuf_RW[orgIdx] = newIdx;
}


Buffer<uint> g_Edge2FaceCountBuf;
Buffer<uint> g_Edge2FaceCountSumBuf;
RWBuffer<uint> g_Edge2FaceCountBuf_RW;
RWBuffer<uint> g_Edge2FaceBuf_RW;

[numthreads( nThread,  1,  1)]  //nFace*3
void cs_FillEdge2FaceCountBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nEntry = 0;
	g_face2EdgeBuf.GetDimensions(nEntry);
	if (nTh >= nEntry)
		return;
	
	uint idx = g_face2EdgeBuf[nTh];
	uint c = 0;
	InterlockedAdd(g_Edge2FaceCountBuf_RW[idx], 1, c);
}

[numthreads( nThread,  1,  1)]  //nFace*3
void cs_FillEdge2FaceBuffer(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nEntry = 0;
	g_face2EdgeBuf.GetDimensions(nEntry);
	if (nTh >= nEntry)
		return;
	uint idx = g_face2EdgeBuf[nTh];
	uint c = 0;
	InterlockedAdd(g_tmpCount_RW[idx], 1, c);
	uint fillIdx = g_Edge2FaceCountSumBuf[idx] - g_Edge2FaceCountBuf[idx] + c;
	g_Edge2FaceBuf_RW[fillIdx] = nTh;
}


RWBuffer<float> g_VertexNormal_RW;
Buffer<float> g_VertexPosition;
[numthreads( nThread,  1,  1)]  //nVertex
void cs_ComputeNormal(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex)
{
	uint nTh = (Gid.z*nGroupY*nGroupX + Gid.y*nGroupX + Gid.x)*nThread + GTid.x;
	uint nVertex = 0;
	g_VertexNormal_RW.GetDimensions(nVertex);
	if (nTh >= nVertex)
		return;



	float3 avgNormal = float3(0,0,0);
		
    uint nNbrFace = g_V2FCountBuffer[nTh];

	uint startIdx = g_V2FCountSumBuffer[nTh] - g_V2FCountBuffer[nTh];

	g_VertexNormal_RW[nTh*3] = nNbrFace;
	g_VertexNormal_RW[nTh*3+1] = startIdx;
	g_VertexNormal_RW[nTh*3+2] = g_V2FIndexBuffer[startIdx + nNbrFace -1];
	
	for (int n =0; n < nNbrFace; n++)
	{
		int nbrFIdx = g_V2FIndexBuffer[startIdx + n];
		uint triIdx[3] = {g_indexBuffer[3*nbrFIdx + 0], g_indexBuffer[3*nbrFIdx + 1], g_indexBuffer[3*nbrFIdx + 2]};
		float3 pt[3] = {
			float3(g_VertexPosition[triIdx[0]*3+0],g_VertexPosition[triIdx[0]*3+1], g_VertexPosition[triIdx[0]*3+2]),
			float3(g_VertexPosition[triIdx[1]*3+0],g_VertexPosition[triIdx[1]*3+1], g_VertexPosition[triIdx[1]*3+2]),
			float3(g_VertexPosition[triIdx[2]*3+0],g_VertexPosition[triIdx[2]*3+1], g_VertexPosition[triIdx[2]*3+2])};
        float3 v1 = pt[1] - pt[0];
		float3 v2 = pt[2] - pt[0];
		
	 	float3 normVec = cross(v1, v2);

		float3 normVec_n = normalize(normVec);;
     	if (isnan(normVec_n.x) || isnan(normVec_n.y) || isnan(normVec_n.z))
			continue;
		avgNormal = avgNormal + normVec_n; 
	
	}
	float3 ret = normalize(avgNormal);
	g_VertexNormal_RW[nTh*3] = ret.x;
	g_VertexNormal_RW[nTh*3+1] = ret.y;
	g_VertexNormal_RW[nTh*3+2] = ret.z;
	return;
}

technique11 Tech_MeshProp {
   pass p0 
	{
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillV2FCount()));
    }
  
   pass p1
	{
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillV2FIndex()));
	}
   pass p2
	{
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillPreEdgeIndexBuffer()));
	}
   pass p3
   {
		SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillPreEdgeIndexDuplicateCountBuffer()));
   }
   pass p4
   {
   		SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillEdgeIndexBuffer()));
   }

   pass p5
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillVertex2EdgeCountBuffer()));
   }
   pass p6
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillVertex2EdgeBuffer()));
   }
   pass p7
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillFace2EdgeBuffer()));
   }
   pass p8
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillEdge2FaceCountBuffer()));
   }

   pass p9
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_FillEdge2FaceBuffer()));
   }
   pass p10
   {
        SetVertexShader( NULL );
        SetGeometryShader( NULL );
        SetPixelShader( NULL );
		SetComputeShader( CompileShader( cs_5_0, cs_ComputeNormal()));
   }
}

